# -*- coding: utf-8 -*-
import json

from FeiShu.FeishuMsg import feishu_msg
from utils import Tools
from utils.logger import log

"""
接口响应处理:
    接口断言
    接口数据提取
"""


class HttpResponse:
    def __init__(self, res=None, response_time_ms=0):
        # 接受request的返回对象
        self.res = res
        self.response_time_ms = response_time_ms
        self.res_obj_data = {
            "status_code": self.status_code,
            "headers": self.headers,
            "cookies": self.cookies,
            "content": self.content,
            "request_body": self.get_request_body()
        }
        log.info("获取接口响应信息:\n%s" % str(
            self.res_obj_data))

    # 获取请求体并进行转码
    def get_request_body(self):
        request_body = str(self.request.body).encode("gbk").decode(
            "unicode_escape")
        if request_body:
            if "&" in request_body:
                return request_body
            elif request_body[0] == '{' and request_body[-1] == "}":
                return eval(request_body)
            else:
                return {}
        else:
            return {}

    def __getattr__(self, key):
        # 获取response中包含的数据
        if key in ["json", "content", "body"]:
            try:
                value = self.res.json()
            except ValueError:
                value = self.res.content
        elif key == 'cookies':
            value = self.res.cookies.get_dict()
        else:
            try:
                value = getattr(self.res, key)
            except AttributeError:
                err_msg = "响应体中没有这个属性: %s" % str(
                    key)
                raise Exception(err_msg)
        self.__dict__[key] = value
        return value

    def _search_jsonpath(self, key):
        """
        获取响应数据中具体某个值的数据
        :param key: 响应体中的参数名
        :return:
        """
        try:
            check_value = Tools.get_target_value(obj=self.res_obj_data, key=key)
            log.info(
                "响应体中对应的值为: %s" % str(check_value))
        except ValueError as ve:
            log.error("没有在响应体中找到这个键:(%s)" % key)
            raise ve
        return check_value

    def extract_value(self, extractors):
        """
        从返回对象中取值
        :param extractors: 传入字典类型，key:value key 代表提取的参数名称，value代表值在res里面的键名
        :return:
        """
        log.info("开始从响应中提取值.")
        # 如果extractors传入一个空值，则返回一个空值
        if not extractors:
            log.info("yaml文件中没有要求提取值.")
            return {}
        extract_map = {}
        log.info("需要提取的数据如下: %s" % str(extractors))
        for key, field in extractors.items():
            if field and field != "None":
                value = self._search_jsonpath(key=field)
                extract_map[key] = value
            else:
                return extract_map
        log.info(
            f"数据提取完成，提取的数据为:\n{json.dumps(extract_map, indent=1, ensure_ascii=False)}")
        return extract_map

    # 断言的封装
    def validate(self, validators):
        # 查看设置的预期结果是否为列表类型，如果不是列表类型则无法进行断言，返回提示消息
        if not isinstance(validators, list):
            log.error("需要断言的数据必须是列表类型.")
            raise TypeError("需要断言的数据必须是列表类型.")
        log.info("断言设置为:\n %s" % str(validators))
        result = True
        comparator_msg = ''
        expected_msg = ''
        for validator in validators:
            # 将参数中，断言的方式以及预期的结果取出
            for comparator, expected in validator.items():
                try:
                    # 当判断是否相等时，相应的处理；yaml文件中expected的格式必须为：[字段名, 预期结果]
                    if comparator in ["eq", "equal", "Equal"]:
                        log.info("断言 %s 是否等于 %s" % (
                            str(expected[0]), str(expected[1])))
                        assert str(expected[1]) == str(Tools.get_target_value(
                            self.res_obj_data, expected[0]))
                    # 当判断是否不相等时，相应的处理
                    elif comparator in ["not_equal", "not_eq", "notEqual"]:
                        log.info(
                            "断言 %s 是否不等于 %s" % (
                                str(expected[0]),
                                str(str(expected[1]))))
                        assert str(expected[1]) != str(Tools.get_target_value(
                            self.res_obj_data, expected[0]))
                    # 当判断响应中是否包含预期结果的处理。此时yaml文件中expected的格式必须为:[匹配的内容]；ps：匹配的范围包含响应的全部内容
                    elif comparator in ["in", "In"]:
                        log.info("断言 %s 是否存在于响应中" % str(expected[0]))
                        assert str(expected[0]) in str(self.res_obj_data)
                    # 当判断响应中是否不包含预期结果的处理
                    elif comparator in ["not_in", "notIn"]:
                        log.info("断言 %s 是否不存在于响应中" % str(expected[0]))
                        assert str(expected[0]) not in str(self.res_obj_data)
                    elif comparator == 'run_time':
                        log.info("断言运行时间是否小于 < %d(单位为ms)" % int(expected))
                        assert self.response_time_ms < int(expected)
                    else:
                        log.error("错误的断言方式!!")
                        raise ValueError("错误的断言方式!!")
                    log.info("断言完成.")
                except AssertionError:
                    result = False
                    comparator_msg = comparator
                    expected_msg = expected
                    log.error("断言失败!!")
                    raise AssertionError("断言失败!!")
                finally:
                    feishu_msg.get_msg_body(result, comparator_msg, expected_msg, self.res_obj_data,
                                            self.response_time_ms)
        return result
